#!/bin/sh
 
# 任何命令失败，立刻退出，并返回非零状态码
set -e

# 第一个传参：执行类型，必填(update:升级部署 restart:重启 rollback:回滚 stop:停止服务)
EXECUTE_TYPE="$1"
# 第二个传参：jar包全名，必填
JAR_FEIL_NAME="$2"
 
# 日期时间
DATE=$(date +%Y%m%d%H%M)

# 当前工作路径，也是jar的运行目录（deploy.sh文件必须和运行中的jar包在同一目录下）
BASE_PATH=$(pwd)

# 新jar包所在的文件夹位置
BUILD_PATH=$BASE_PATH/build

# 备份旧jar包的文件夹位置
BACKUP_PATH=$BASE_PATH/backup

# java jvm 启动参数
JAVA_OPS="-Xms2g -Xmx10g -Xmn1g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC -verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:-OmitStackTraceInFastThrow"

# 端口号 读取bootstrap.yml文件第2行 第二列的字段值（空格分列）
PROT=$(awk 'NR==2' bootstrap.yml | awk '{print $2}')

# 健康检查 URL，服务还未支持健康检查，此处先忽略
# HEALTH_CHECK_URL=http://127.0.0.1:$PROT/actuator/health/

# 运行中的java服务进程 PID
function getPid() {
    PID=$(ps -ef | grep $JAR_FEIL_NAME | grep -v "grep" | grep -v "deploy" | awk '{print $2}')
}
getPid

SPACES=" "


#必须指定要部署的jar包，否则报错退出
if [ ! -n "$JAR_FEIL_NAME" ]; then
    echo -e "[ERROR] the second param JAR_FEIL_NAME is necessary. as:./deploy.sh deploy FMP-XXXX-WEB-V1.0.0.jar.";
    exit 1;
fi

if [ "$EXECUTE_TYPE" == "update" ]; then
    # 如果是升级部署服务没有找到构建好的新包，则报错并退出
    if [ ! -f "$BUILD_PATH/$JAR_FEIL_NAME" ]; then
        echo -e "[ERROR] the new jar is not in directory:$BUILD_PATH."
        exit 1;
    fi
fi

# 打印相关信息
function printInfo() {
    echo "[DEPLOY_INFO] EXECUTE_TYPE: $EXECUTE_TYPE"
    echo "[DEPLOY_INFO] BASE_PATH: $BASE_PATH"
    echo "[DEPLOY_INFO] BUILD_PATH: $BUILD_PATH"
    echo "[DEPLOY_INFO] BACKUP_PATH: $BACKUP_PATH"
    echo "[DEPLOY_INFO] JAR_FEIL_NAME: $JAR_FEIL_NAME"
    echo "[DEPLOY_INFO] PROT: $PROT"
    echo "[DEPLOY_INFO] PID: $PID"
}


 
# 备份 将服务运行目录中的 jar 包备份到 backup 目录下
function backup() {
    echo -e "[BACKUP] to be backup."
    # 如果不存在，则无需备份
    if [ ! -f "$BASE_PATH/$JAR_FEIL_NAME" ]; then
        echo -e "[BACKUP] the old jar does not exist,skip backup."
    else
        # 如果backup目录不存在，则创建目录
        if [ ! -d $BACKUP_PATH ];then
            echo -e "[BACKUP] create directory named backup."
            mkdir -p $BACKUP_PATH
        fi

        if [ -f "$BACKUP_PATH/$JAR_FEIL_NAME" ]; then
            # 备份前先清空backup目录
            echo -e "[BACKUP] clear backup directory."
            rm -r $BACKUP_PATH/*
        fi
        
        # 将运行目录下的jar包复制到 backup 目录下
        echo -e "[BACKUP] copy the old jar to $BACKUP_PATH."
        cp $BASE_PATH/$JAR_FEIL_NAME $BACKUP_PATH/JAR_FEIL_NAME
        echo -e "[BACKUP] backup completed."
    fi
}

# 将备份的jar包还原到运行目录下（用于回滚）
function restoreJarFile() {
    echo -e "[RESTORE_JAR_FILE] to be restore the jar"
    # 如果不存在，则无需还原，直接退出
    if [ ! -f "$BACKUP_PATH/$JAR_FEIL_NAME" ]; then
        echo -e "[RESTORE_JAR_FILE] error: the backup jar does not exist."
        exit 1;
    else
        # 删除运行目录中的 jar 包
        echo -e "[RESTORE_JAR_FILE] remove the jar:$BASE_PATH/$JAR_FEIL_NAME."
        if [ -f "$BASE_PATH/$JAR_FEIL_NAME" ]; then
            rm $BASE_PATH/$JAR_FEIL_NAME
            echo -e "[RESTORE_JAR_FILE] removed."
        else
            echo -e "[RESTORE_JAR_FILE] the work jar does not exist, skip remove." 
        fi
        
        # 将 backup 目录下备份的jar，还原到工作目录中
        echo -e "[RESTORE_JAR_FILE] copy the backup jar to $BASE_PATH."
        cp $BACKUP_PATH/JAR_FEIL_NAME $BASE_PATH

        # 移除bacup目录下的jar包
        rm $BACKUP_PATH/$JAR_FEIL_NAME
        echo -e "[RESTORE_JAR_FILE] clear backup directory."
        echo -e "[RESTORE_JAR_FILE] restore completed."
    fi
}

# 停止服务: 将运行中的 Java 服务进程，进行关闭，如果从未启动则会跳过
function stop() {
    echo -e "[STOP] to be stop server by PID: $PID"
    # 如果 Java 服务启动中，则进行关闭
    if [ -n "$PID" ]; then
        # 正常关闭
        echo -e "[STOP] try kill -15 $PID"
        kill -15 $PID
        echo -e "[STOP] stopping\c"
        # 等待最大 60 秒，直到关闭完成。
        for ((i = 0; i < 60; i++))
            do  
                getPid
                if [ -n "$PID" ]; then
                    rem=$(($i % 3))
                    if [ "$rem" == "0" ]; then
                        echo -e "\r\c"
                        echo -e "[STOP] stopping\c"
                    fi
                    # -e 表示启用转义，\c表示不换行，效果就是每秒钟在屏幕的同一行不断向后输出“.”这个符号
                    echo -e ".\c"
                    sleep 1
                else
                    # 换行
                    echo ""
                    echo -e "[STOP] stopped."
                    break
                fi
             done
        
        # 如果正常关闭失败，那么进行强制 kill -9 进行关闭
        if [ -n "$PID" ]; then
            echo -e "[STOP] kill -15 failed."
            echo -e "[STOP] forced shutdown： kill -9 $PID."
            kill -9 $PID
            echo -e "[STOP] stopped."
        fi
    # 如果 Java 服务未启动，则无需关闭
    else
        echo -e "[STOP] server not run, skip stop."
    fi
}


# 将最新构建的jar包 移动到待运行的目录下
function replace() {
    echo -e "[REPLACE] to be replace the jar."
 
    # 删除原 jar 包
    echo -e "[REPLACE] remove the old jar."
    if [ -f "$BASE_PATH/$JAR_FEIL_NAME" ]; then
        rm $BASE_PATH/$JAR_FEIL_NAME
        echo -e "[REPLACE] removed."
    else
        echo -e "[REPLACE] the old jar does not exist, skip remove." 
    fi
 
    # 复制新 jar 包
    echo -e "[REPLACE] copy new jar to $BASE_PATH"
    cp $BUILD_PATH/$JAR_FEIL_NAME $BASE_PATH
    echo -e "[REPLACE] copied."

    # 移除新包（防止不小心重复执行更新）
    echo -e "[REPLACE] remove build directory jar."
    rm $BUILD_PATH/$JAR_FEIL_NAME
    echo -e "[REPLACE] removed."
    echo -e "[REPLACE] replaced."
}
 
# 启动 使用新的 jar 包，启动 Java 服务
function start() {
    echo -e "[START] to be startup the server."
     # 启动前，打印启动命令
    echo -e "[START] execute command: java -server $JAVA_OPS -jar $JAR_FEIL_NAME --spring.config.location=$BASE_PATH/bootstrap.yml >/dev/null 2>&1 &"
    # 执行启动命令，将输出重定向到 /dev/null 并后台运行
    nohup java -server $JAVA_OPS -jar $JAR_FEIL_NAME --spring.config.location=$BASE_PATH/bootstrap.yml >/dev/null 2>&1 &
    echo -e "[START] starting..."
}
 
# 健康检查 通过健康检查 URL ，判断 Java 服务是否启动成功,如果未配置url，则跳过健康检查
function healthCheck() {
    # 如果配置健康检查，则进行健康检查
    if [ -n "$HEALTH_CHECK_URL" ]; then
        # 健康检查持续 180 秒，直到健康检查通过或超时认定为失败
        echo -e "[HEALTH_CHECK] check the startup status\c";
        for ((i = 0; i < 180; i++))
            do
                # 请求健康检查地址，只获取状态码。
                result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"`
                # 如果状态码为 200，则说明健康检查通过
                if [ "$result" == "200" ]; then
                    echo "";
                    echo -e "[HEALTH_CHECK] successfully started.";
                    break
                # 如果状态码非 200，则说明未通过。sleep 1 秒后，继续重试
                else
                    rem=$(($i % 3))
                    if [ "$rem" == "0" ]; then
                        echo -e "\r\c"
                        echo -e "[HEALTH_CHECK] check the startup status\c";
                    fi
                    echo -e ".\c"
                    sleep 1
                fi
            done
        
        # 健康检查未通过，则异常退出 shell 脚本，不继续部署。
        if [ ! "$result" == "200" ]; then
            echo -e "[HEALTH_CHECK] fail to start!!!";
            exit 1;
        # 健康检查通过，服务运行中。
        else
            echo -e "[HEALTH_CHECK] successfully started.";
        fi
    # 如果未配置健康检查，则人工看日志是否部署成功。
    else
        echo -e "[HEALTH_CHECK] health check is currently not supported";
        echo -e "[HEALTH_CHECK] please check startup result through logs or nacos";
    fi
}

function status() {
    if [ -n "$PID" ]; then
        echo -e "[STATUS] runing"
        echo -e "[STATUS] PROT:$PROT"
        echo -e "[STATUS] PID:$PID"
    else
        echo -e "[STATUS] not runing"
    fi
}
 
# 升级部署新jar包
function update() {
    echo ""
    # 打印相关信息
    printInfo
    echo ""
    # 备份jar包
    backup
    echo ""
    # 停止当前的 Java 服务
    stop
    echo ""
    # 替换新的 jar
    replace
    echo ""
    # 启动 Java 服务
    start
    echo ""
    # 健康检查
    healthCheck
}

# 重启服务
function restart() {
    echo ""
    # 打印相关信息
    printInfo
    echo ""
    # 停止当前的 Java 服务
    stop
    echo ""
    # 启动 Java 服务
    start
    echo ""
    # 健康检查
    healthCheck
}

# 回滚到升级前
function rollBack() {
    echo ""
    # 打印相关信息
    printInfo
    echo ""
    # 停止当前的 Java 服务
    stop
    echo ""
    # 将备份的jar包还原到待运行目录
    restoreJarFile
    echo ""
    # 启动 Java 服务
    start
    echo ""
    # 健康检查
    healthCheck
}

if [ "$EXECUTE_TYPE" == "update" ]; then
    update
elif [ "$EXECUTE_TYPE" == "restart" ]; then
    restart
elif [ "$EXECUTE_TYPE" == "rollBack" ]; then
    rollBack
elif [ "$EXECUTE_TYPE" == "stop" ]; then
    stop
elif [ "$EXECUTE_TYPE" == "status" ]; then
    status
else
    echo -e "[EXECUTE_TYPE] the first param EXECUTE_TYPE is necessary. as:./deploy.sh update FMP-XXXX-WEB-V1.0.0.jar"
    echo -e "[EXECUTE_TYPE] the first param EXECUTE_TYPE can be update、restart、rollBack、stop or status."
fi

echo -e "[END] done.";